import os
import time
import stat
import json
import shutil
import zipfile
import xml.etree.ElementTree as ET
from datetime import datetime
from utils import *
from constants import *
from collections import defaultdict
from jinja2 import Template


OUT_TEST_PATH = "out/rk3568/tests/unittest/ace_engine/ace_engine"
LAST_UNAVAILABLE = []
LAST_FAILED = []

class RunTestsHandler():
    def __init__(self, code_path: str, update_code_time):
        self.code_path = code_path
        self.current_time = datetime.now().strftime("%Y-%m-%d-%H")
        self.update_code_time = update_code_time

    def get_all_test_suite_path(self):
        test_suite_path_list = []
        test_suite_name_list = []
        test_path = os.path.join(self.code_path, OUT_TEST_PATH)
        if os.path.exists(test_path):
            for root, _, files in os.walk(test_path):
                for file_name in files:
                    file_path = os.path.join(root, file_name)
                    _, ext = os.path.splitext(file_path)
                    if ext == '':
                        test_suite_name_list.append(file_name)
                        test_suite_path_list.append(file_path)
        else:
            Logger.info("The test path not exists")
        return test_suite_path_list, test_suite_name_list

    def run_all_test_suite(self, test_suite_path_list, test_suite_name_list):
        # 全部先push到/data/test目录下
        Logger.info("start execute all test suites")
        start = time.time()
        os.system("hdc shell rm -f /data/log/faultlog/*")
        os.system("hdc shell rm -f /data/log/faultlog/temp/*")
        os.system("hdc shell rm -f /data/log/faultlog/debug/*")
        os.system("hdc shell rm -f /data/log/faultlog/faultlogger/*")
        os.system("hdc shell hilog -w stop")
        os.system("hdc shell hilog -r")
        os.system("hdc shell rm -rf /data/test/")
        os.system("hdc shell mkdir -p /data/test/")
        os.system("rm -rf results")
        for index in range(len(test_suite_path_list)):
            os.system("hdc file send {} /data/test".format(test_suite_path_list[index]))
            os.system("hdc shell chmod +x /data/test/{}".format(test_suite_name_list[index]))
            execute_cmd = "hdc shell /data/test/{} --gtest_output=xml:/data/test/results/{}.xml"
            os.system(execute_cmd.format(test_suite_name_list[index], test_suite_name_list[index]))
        # 将结果全部导出，得到当前的时间
        self.current_time = datetime.now().strftime("%Y-%m-%d-%H")
        end = time.time()
        execute_time = end - start
        # 导出到目录前先删除
        os.system("hdc file recv /data/test/results .")
        os.system(f"hdc file recv /data/log/faultlog ./backup/{self.current_time}")
        Logger.info("end executing all test suites")
        return execute_time

    def _parse_xml(self, xml_file_path, test_module_name):
        tree = ET.parse(xml_file_path)
        root = tree.getroot()
        tests = root.attrib.get("tests")
        failures = root.attrib.get("failures")
        failed_info = {
            "test_module_name": test_module_name,
            "total_count": tests,
            "failed_count": failures,
            "failed_testcases": []
        }
        passed_info = {
            "test_module_name": test_module_name,
            "total_count": tests,
            "passed_count": 0,
            "passed_testcase_name": []
        }
        passed_count = 0
        for testsuite in root.findall(".//testsuite"):
            testsuite_name = testsuite.attrib.get("name")
            testsuite_failures = testsuite.attrib.get("failures")
            for testcase in testsuite.findall(".//testcase"):
                testcase_name = testcase.attrib.get("name")
                failure = testcase.find("failure")
                if failure is not None:
                    messages = failure.text.split('\n')
                    fail_test_case_info = {
                        "name": "{}#{}".format(testsuite_name, testcase_name),
                        "message": messages
                    }
                    failed_info["failed_testcases"].append(fail_test_case_info)
                else:
                    passed_info["passed_testcase_name"].append("{}#{}".format(testsuite_name, testcase_name))
                    passed_count = passed_count+1
        passed_info["passed_count"] = str(passed_count)
        return failed_info, passed_info

    def _is_file_exist_and_has_content(self, file_path: str) -> bool:
        try:
            if os.path.exists(file_path):
                file_size = os.path.getsize(file_path)
                if file_size > 0:
                    return True
        except Exception as e:
            Logger.error("judge file exist and has content failed")
        return False

    def parse_result(self, test_suite_name_list, execute_time):
        # 处理所有的xml拿到本次的执行结果
        Logger.info("start parse result")
        current_path = os.getcwd()
        total_tests_count = 0
        failed_tests_count = 0
        passed_tests_count = 0
        test_result = {
            "time_stamp": str(datetime.now()),
            "execute_time": 0,
            "total_execute_tests": 0,
            "failed_tests_count": 0,
            "passed_tests_count": 0,
            "unavailable": [],
            "failed": [],
            "passed": []
        }
        for test_suite_name in test_suite_name_list:
            xml_result_path = os.path.join(current_path, f"results/{test_suite_name}.xml")
            if self._is_file_exist_and_has_content(xml_result_path):
                failed_info, passed_info = self._parse_xml(xml_result_path, test_suite_name)
                total_tests_count = total_tests_count + int(failed_info.get('total_count', '0'))
                failed_tests_count = failed_tests_count + int(failed_info.get('failed_count', '0'))
                passed_tests_count = passed_tests_count + int(passed_info.get('passed_count', '0'))
                if int(failed_info.get('failed_count', '0')):
                    test_result['failed'].append(failed_info)
                test_result['passed'].append(passed_info)
            else:
                test_result["unavailable"].append(test_suite_name)
                Logger.error(f"The suite {test_suite_name} crashed")
        test_result["execute_time"] = "{} seconds".format(round(execute_time, 2))
        test_result['total_execute_tests'] = total_tests_count
        test_result['failed_tests_count'] = failed_tests_count
        test_result['passed_tests_count'] = passed_tests_count
        try:
            json_file_path = os.path.join(f"./backup/{self.current_time}/", "test_result.json")
            print(json_file_path)
            flags = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
            mode = stat.S_IRUSR | stat.S_IWUSR
            with os.fdopen(os.open(json_file_path, flags, mode), 'w') as json_file:
                json.dump(test_result, json_file, indent=2)
            Logger.info(f"The test results have been generated, path is {json_file_path}")
            return test_result
        except:
            Logger.error(f"The test_result.json file not exists")
            return None

    def _generate_data(self, test_result):
        global LAST_FAILED, LAST_UNAVAILABLE
        compare_data = []
        new_data = []
        # 获取本次的crash和失败
        current_unavailable = test_result['unavailable']
        current_failed = test_result['failed']
        # 生成crash的比较数据
        for suite in current_unavailable:
            new_data.append({
                "test_case": suite,
                "fail_count": "all",
                "fail_name": "all",
                "status": "崩溃",
                "color": "red"
            })
            if suite not in LAST_UNAVAILABLE:
                compare_data.append({
                    "test_case": suite,
                    "fail_count": "all",
                    "fail_name": "all",
                    "status": "崩溃",
                    "color": "red"
                })

        # 先取出上一次的所有失败的module
        last_failed_module = {}
        for failed_suite in LAST_FAILED:
            test_module_name = failed_suite['test_module_name']
            last_failed_testcases_name = []
            for test_case in failed_suite['failed_testcases']:
                last_failed_testcases_name.append(test_case['name'])
            last_failed_module[test_module_name] = last_failed_testcases_name

        # 在用本次的进行对比
        for failed_suite in current_failed:
            # 这里先生成最新的数据
            current_failed_testcases_name = []
            test_module_name = failed_suite['test_module_name']
            failed_count = failed_suite['failed_count']
            for test_case in failed_suite["failed_testcases"]:
                current_failed_testcases_name.append(test_case["name"])
            new_data.append({
                "test_case": test_module_name,
                "fail_count": failed_count,
                "fail_name": "<br>".join(current_failed_testcases_name),
                "status": "失败",
                "color": "orange"
            })
            # 再去比较前一次的
            if test_module_name not in last_failed_module.keys():
                # 如果本次失败的module不在上一次的，则判定为完全新增的失败module
                compare_data.append({
                    "test_case": test_module_name,
                    "fail_count": failed_count,
                    "fail_name": "<br>".join(current_failed_testcases_name),
                    "status": "失败",
                    "color": "orange"
                })
            else:
                # 如果本次失败的module在上一次的，则需要判断失败的用例是否完全相同
                compare_failed_test_cases_name = []
                compare_failed_count = 0
                last_failed_testcases_name = last_failed_module[test_module_name]
                for current_failed_testcase in current_failed_testcases_name:
                    if current_failed_testcase not in last_failed_testcases_name:
                        compare_failed_test_cases_name.append(current_failed_testcase)
                        compare_failed_count = compare_failed_count + 1
                if compare_failed_count != 0:
                    compare_data.append({
                        "test_case": test_module_name,
                        "fail_count": compare_failed_count,
                        "fail_name": "<br>".join(compare_failed_test_cases_name),
                        "status": "失败",
                        "color": "orange"
                    })
        # 最后再把本次的失败更新到上次失败
        LAST_FAILED = current_failed
        LAST_UNAVAILABLE = current_unavailable
        return new_data, compare_data

    def compress_back_up_result_to_zip(self):
        Logger.info("start compress back up result to zip")
        dir_path = os.path.join("./backup/", self.current_time)
        zip_path = os.path.join("./backup/", f"{self.current_time}.zip")
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, files in os.walk(dir_path):
                for file in files:
                    full_path = os.path.join(root, file)
                     # 使用相对路径，使 ZIP 文件不包含多余的目录层级
                    arcname = os.path.relpath(full_path, dir_path)
                    zipf.write(full_path, arcname)
        # 删除原始目录
        os.system(f"rm -rf {dir_path}")
        Logger.info(f"The {dir_path} already compressed to {zip_path}")

    def send_owners_email(self, test_result):
        Logger.info("start send email to owners")
        email_utils = EmailUtils()
        email_utils.login_smtp_server()
        total_tests_count = test_result['total_execute_tests']
        failed_tests_count = test_result['failed_tests_count']
        new_data, compare_data = self._generate_data(test_result)
        template = Template(HTML_TEMPLATE)
        result_content = template.render(
            new_data=new_data,
            compare_data=compare_data,
            timestamp=self.current_time,
            code_time=self.update_code_time,
            totoal_count=total_tests_count,
            unavilable_count=len(test_result['unavailable']),
            failed_count=failed_tests_count
        )
        zip_path = f"./backup/{self.current_time}.zip"
        email_utils.send_email_with_zip(
            TDD_INTERFACE_PERSON_LIST,
            result_content,
            zip_path,
            cc=TDD_PL_LIST,
            bcc=ONE_GROUP_LIST,
        )
        email_utils.close()
        os.system(f"rm {zip_path}")
        Logger.info("end send email")

    def process(self):
        test_suite_path_list, test_suite_name_list = self.get_all_test_suite_path()
        execution_time = self.run_all_test_suite(test_suite_path_list, test_suite_name_list)
        test_result = self.parse_result(test_suite_name_list, execution_time)
        if test_result != None:
            self.compress_back_up_result_to_zip()
            self.send_owners_email(test_result)
        else:
            email_utils = EmailUtils()
            email_utils.login_smtp_server()
            email_utils.send_mail("liyujie43@huawei.com", "执行失败，没有获取到执行结果")
            email_utils.close()

    def debug_process(self):
        test_suite_path_list, test_suite_name_list = self.get_all_test_suite_path()
        execution_time = self.run_all_test_suite(test_suite_path_list, test_suite_name_list)
        test_result = self.parse_result(test_suite_name_list, execution_time)
        if test_result != None:
            self.compress_back_up_result_to_zip()
            total_tests_count = test_result['total_execute_tests']
            failed_tests_count = test_result['failed_tests_count']
            print(total_tests_count)
            print(failed_tests_count)
